#include "sync_stats.h"

static esp_err_t print_real_time_stats(TickType_t xTicksToWait)
{
    TaskStatus_t *start_array = NULL, *end_array = NULL;
    UBaseType_t start_array_size, end_array_size;
    uint32_t start_run_time, end_run_time;
    esp_err_t ret;

    //Allocate array to store current task states
    start_array_size = uxTaskGetNumberOfTasks() + ARRAY_SIZE_OFFSET;
    start_array = malloc(sizeof(TaskStatus_t) * start_array_size);
    if (start_array == NULL) {
        ret = ESP_ERR_NO_MEM;
        printf("err1\r\n");
        goto exit;
    }
    //Get current task states
    start_array_size = uxTaskGetSystemState(start_array, start_array_size, &start_run_time);
    if (start_array_size == 0) {
        ret = ESP_ERR_INVALID_SIZE;
        printf("err2\r\n");
        goto exit;
    }

    vTaskDelay(xTicksToWait);

    //Allocate array to store tasks states post delay
    end_array_size = uxTaskGetNumberOfTasks() + ARRAY_SIZE_OFFSET;
    end_array = malloc(sizeof(TaskStatus_t) * end_array_size);
    if (end_array == NULL) {
        ret = ESP_ERR_NO_MEM;
        printf("err3\r\n");
        goto exit;
    }
    //Get post delay task states
    end_array_size = uxTaskGetSystemState(end_array, end_array_size, &end_run_time);
    if (end_array_size == 0) {
        ret = ESP_ERR_INVALID_SIZE;
        printf("err4\r\n");
        goto exit;
    }

    //Calculate total_elapsed_time in units of run time stats clock period.
    uint32_t total_elapsed_time = (end_run_time - start_run_time);
    if (total_elapsed_time == 0) {
        ret = ESP_ERR_INVALID_STATE;
        printf("err5 %d %d\r\n", end_run_time, start_run_time);
        goto exit;
    }

    printf("\n\n| Task | Run Time | Percentage\n");
    //Match each task in start_array to those in the end_array
    for (int i = 0; i < start_array_size; i++) {
        int k = -1;
        for (int j = 0; j < end_array_size; j++) {
            if (start_array[i].xHandle == end_array[j].xHandle) {
                k = j;
                //Mark that task have been matched by overwriting their handles
                start_array[i].xHandle = NULL;
                end_array[j].xHandle = NULL;
                break;
            }
        }
        //Check if matching task found
        if (k >= 0) {
            uint32_t task_elapsed_time = end_array[k].ulRunTimeCounter - start_array[i].ulRunTimeCounter;
            uint32_t percentage_time = (task_elapsed_time * 100UL) / (total_elapsed_time * portNUM_PROCESSORS);
            printf("| %s | %d | %d%%\n", start_array[i].pcTaskName, task_elapsed_time, percentage_time);
        }
    }


    //Print unmatched tasks
    for (int i = 0; i < start_array_size; i++) {
        if (start_array[i].xHandle != NULL) {
            printf("| %s | Deleted\n", start_array[i].pcTaskName);
        }
    }
    for (int i = 0; i < end_array_size; i++) {
        if (end_array[i].xHandle != NULL) {
            printf("| %s | Created\n", end_array[i].pcTaskName);
        }
    }

    char pWriteBuffer[2048];
    vTaskList((char *)&pWriteBuffer);
    printf("\ntask_name   task_state  priority   stack  tasK_num\n");
    printf("%s\n", pWriteBuffer);

    ret = ESP_OK;

exit:    //Common return path
    free(start_array);
    free(end_array);
    return ret;
}



void stats_task(void *arg)
{

    //Print real time stats periodically
    while (1) {
//        printf("\n\nGetting real time stats over %d ticks\n", STATS_TICKS);
        if (print_real_time_stats(STATS_TICKS) == ESP_OK) {
//            printf("Real time stats obtained\n");
        } else {
            printf("Error getting real time stats\n");
        }
        uint32_t totalfreeRAM = esp_get_free_heap_size()/1024;
        uint32_t freeRAM = heap_caps_get_free_size(MALLOC_CAP_INTERNAL)/1024;
        uint32_t freeDRAM = heap_caps_get_free_size(MALLOC_CAP_INTERNAL | MALLOC_CAP_8BIT)/1024;
        uint32_t minimuntotalfreeRAM = esp_get_minimum_free_heap_size()/1024;
        uint32_t minimunfreeRAM = heap_caps_get_minimum_free_size(MALLOC_CAP_INTERNAL)/1024;
        printf("totalfreeRAM: %d, freeRAM: %d, freeDRAM: %d, minimuntotalfreeRAM: %d, minimunfreeRAM: %d(in kB)\r\n", totalfreeRAM, freeRAM, freeDRAM, minimuntotalfreeRAM, minimunfreeRAM);
        vTaskDelay(pdMS_TO_TICKS(1000));
    }
}

